{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "919b31e1",
   "metadata": {},
   "source": [
    "# 异常与警告\n",
    "\n",
    "## 异常"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb73f95d",
   "metadata": {},
   "source": [
    "写代码的时候，出现错误必不可免。\n",
    "\n",
    "看下面这段代码：\n",
    "\n",
    "```python\n",
    "import math\n",
    "\n",
    "while True:\n",
    "    text = input('> ')\n",
    "    if text[0] == 'q':\n",
    "        break\n",
    "    x = float(text)\n",
    "    y = math.log10(x)\n",
    "    print(f\"log10({x}) = {y}\")\n",
    "```\n",
    "\n",
    "这段代码接收命令行的输入，输入为数字时，计算它的对数并输出，直到输入值为 q 为止。\n",
    "\n",
    "乍看没什么问题，然而输入0或者负数时："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ce29786e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> -1\n"
     ]
    },
    {
     "ename": "ValueError",
     "evalue": "math domain error",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36m<cell line: 4>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      6\u001b[0m     \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[1;32m      7\u001b[0m x \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mfloat\u001b[39m(text)\n\u001b[0;32m----> 8\u001b[0m y \u001b[38;5;241m=\u001b[39m \u001b[43mmath\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlog10\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m      9\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlog10(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) = \u001b[39m\u001b[38;5;132;01m{\u001b[39;00my\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n",
      "\u001b[0;31mValueError\u001b[0m: math domain error"
     ]
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    text = input('> ')\n",
    "    if text[0] == 'q':\n",
    "        break\n",
    "    x = float(text)\n",
    "    y = math.log10(x)\n",
    "    print(f\"log10({x}) = {y}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5cac91cd",
   "metadata": {},
   "source": [
    "log10 函数会报错，因为不能接受非正值。\n",
    "\n",
    "一旦报错，程序就会停止执行，如果不希望程序停止执行，那么可以添加一对 try & except："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "c8fe326c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> -1\n",
      "the value must be greater than 0\n",
      "> 0\n",
      "the value must be greater than 0\n",
      "> 1\n",
      "log10(1.0) = 0.0\n",
      "> q\n"
     ]
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = math.log10(x)\n",
    "        print(f\"log10({x}) = {y}\")\n",
    "    except ValueError:\n",
    "        print(\"the value must be greater than 0\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83b73dea",
   "metadata": {},
   "source": [
    "## 捕捉不同的异常类型\n",
    "假设将这里的 y 更改为 1 / math.log10(x)，此时输入 1："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d7c2c68d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> 1\n"
     ]
    },
    {
     "ename": "ZeroDivisionError",
     "evalue": "float division by zero",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
      "Input \u001b[0;32mIn [3]\u001b[0m, in \u001b[0;36m<cell line: 4>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      7\u001b[0m         \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[1;32m      8\u001b[0m     x \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mfloat\u001b[39m(text)\n\u001b[0;32m----> 9\u001b[0m     y \u001b[38;5;241m=\u001b[39m \u001b[38;5;241;43m1\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mmath\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlog10\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     10\u001b[0m     \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m1 / log10(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) = \u001b[39m\u001b[38;5;132;01m{\u001b[39;00my\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m     11\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m:\n",
      "\u001b[0;31mZeroDivisionError\u001b[0m: float division by zero"
     ]
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(f\"1 / log10({x}) = {y}\")\n",
    "    except ValueError:\n",
    "        print(\"the value must be greater than 0\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a6b9562",
   "metadata": {},
   "source": [
    "程序仍然抛出了异常，原因是`ZeroDivisionError`不在可处理的异常中。\n",
    "\n",
    "可以有两种方法处理这个问题，第一种是捕获异常的父类`Exception`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "65df118a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> 1\n",
      "invalid value\n",
      "> 0\n",
      "invalid value\n",
      "> -1\n",
      "invalid value\n",
      "> q\n"
     ]
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(f\"1 / log10({x}) = {y}\")\n",
    "    except Exception:\n",
    "        print(\"invalid value\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc05d94b",
   "metadata": {},
   "source": [
    "第二种是指定多个错误类型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c6c68cea",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> 1\n",
      "invalid value\n",
      "> 0\n",
      "invalid value\n",
      "> -1\n",
      "invalid value\n",
      "> q\n"
     ]
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(f\"1 / log10({x}) = {y}\")\n",
    "    except (ZeroDivisionError, ValueError):\n",
    "        print(\"invalid value\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3de2f776",
   "metadata": {},
   "source": [
    "还可以分开处理："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "57fd9ccf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> -1\n",
      "the value must be greater than 0\n",
      "> 1\n",
      "the value must not be 1\n",
      "> 2\n",
      "1 / log10(2.0) = 3.321928094887362\n",
      "> q\n"
     ]
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(f\"1 / log10({x}) = {y}\")\n",
    "    except ValueError:\n",
    "        print(\"the value must be greater than 0\")\n",
    "    except ZeroDivisionError:\n",
    "        print(\"the value must not be 1\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38774e9b",
   "metadata": {},
   "source": [
    "还可以将异常的具体信息打出来："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "716d8331",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> -1\n",
      "math domain error\n",
      "> 0\n",
      "math domain error\n",
      "> 1\n",
      "float division by zero\n",
      "> abcde\n",
      "could not convert string to float: 'abcde'\n",
      "> q\n"
     ]
    }
   ],
   "source": [
    "import math\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        text = input('> ')\n",
    "        if text[0] == 'q':\n",
    "            break\n",
    "        x = float(text)\n",
    "        y = 1 / math.log10(x)\n",
    "        print(f\"1 / log10({x}) = {y}\")\n",
    "    except Exception as e:\n",
    "        print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73dd78c8",
   "metadata": {},
   "source": [
    "可以用raise主动抛出异常，例如判断月份是否在1-12之间："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "01c60d1c",
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "13 must between 1 and 12!",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "Input \u001b[0;32mIn [8]\u001b[0m, in \u001b[0;36m<cell line: 3>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m month \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m13\u001b[39m\n\u001b[1;32m      3\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;241m1\u001b[39m \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m month \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m12\u001b[39m:\n\u001b[0;32m----> 4\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmonth\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m must between 1 and 12!\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
      "\u001b[0;31mValueError\u001b[0m: 13 must between 1 and 12!"
     ]
    }
   ],
   "source": [
    "month = 13\n",
    "\n",
    "if not 1 <= month <= 12:\n",
    "    raise ValueError(f\"{month} must between 1 and 12!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e16d9e79",
   "metadata": {},
   "source": [
    "## finally\n",
    "\n",
    "try/catch 块还有一个可选的关键词 finally。\n",
    "\n",
    "不管 try 块有没有异常， finally 块的内容总是会被执行，而且会在抛出异常前执行，因此可以用来作为安全保证，比如确保打开的文件被关闭："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "072d0ac9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "finally was called.\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    print(1)\n",
    "finally:\n",
    "    print('finally was called.')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "91acd337",
   "metadata": {},
   "source": [
    "如果有异常被抛出，finally的部分会在抛出异常前执行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "5f8e070c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "finally was called.\n"
     ]
    },
    {
     "ename": "ZeroDivisionError",
     "evalue": "division by zero",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
      "Input \u001b[0;32mIn [11]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m----> 2\u001b[0m     \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m)\n\u001b[1;32m      3\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m      4\u001b[0m     \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mfinally was called.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n",
      "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    print(1 / 0)\n",
    "finally:\n",
    "    print('finally was called.')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca5dfe89",
   "metadata": {},
   "source": [
    "异常被处理了，则在最后执行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "1cdb21e5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "division by zero\n",
      "finally was called.\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    print(1 / 0)\n",
    "except Exception as e:\n",
    "    print(e)\n",
    "finally:\n",
    "    print('finally was called.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5fe4fb61",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
